package cn.schoolwow.quickdao.module.entity.flow.annotation;

import cn.schoolwow.quickdao.annotation.*;
import cn.schoolwow.quickdao.domain.QuickDAOConfig;
import cn.schoolwow.quickdao.domain.database.ddl.IndexField;
import cn.schoolwow.quickdao.domain.database.parent.DatabaseType;
import cn.schoolwow.quickdao.domain.entity.Entity;
import cn.schoolwow.quickdao.domain.entity.Property;
import cn.schoolwow.quickdao.util.StringUtil;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class GetPropertyAnnotationFlow implements BusinessFlow {
    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        Entity entity = (Entity) flowContext.checkData("entity");

        Property property = new Property();
        property.entity = entity;
        flowContext.putTemporaryData("property", property);

        setColumnType(flowContext);
        setConstraint(flowContext);
        setIndexField(flowContext);
    }

    @Override
    public String name() {
        return "提取字段注解";
    }

    private void setColumnType(FlowContext flowContext){
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        DatabaseType databaseType = flowContext.checkInstanceData(DatabaseType.class);
        Field field = (Field) flowContext.checkData("field");
        Property property = (Property) flowContext.checkData("property");

        if (null != field.getAnnotation(ColumnName.class)) {
            property.column = field.getAnnotation(ColumnName.class).value();
        } else {
            property.column = StringUtil.camel2Underline(field.getName());
        }
        property.name = field.getName();
        property.columnLabel = property.name;
        property.clazz = field.getType();
        property.className = field.getType().getName();
        if (null != field.getAnnotation(ColumnType.class)) {
            property.columnType = field.getAnnotation(ColumnType.class).value();
        } else if(null != field.getAnnotation(ColumnTypeRepeatable.class)){
            ColumnTypeRepeatable columnTypeRepeatable = field.getAnnotation(ColumnTypeRepeatable.class);
            for(ColumnType columnType:columnTypeRepeatable.value()){
                if(columnType.databaseType().equals(databaseType)){
                    property.columnType = columnType.value();
                    break;
                }
            }
            if(null==property.columnType){
                for(ColumnType columnType:columnTypeRepeatable.value()){
                    if(columnType.databaseType().equals(DatabaseType.CURRENT)){
                        property.columnType = columnType.value();
                        break;
                    }
                }
            }
        } else {
            property.columnType = quickDAOConfig.databaseContext.databaseProvider.convertJavaTypeToColumnType(property.className);
        }
        if (property.columnType.contains("(") && property.columnType.contains(")")) {
            property.range = property.columnType.substring(property.columnType.indexOf("(") + 1, property.columnType.indexOf(")"));
            property.columnType = property.columnType.substring(0, property.columnType.indexOf("("));
        }
        if (null != field.getDeclaredAnnotation(Comment.class)) {
            property.comment = field.getDeclaredAnnotation(Comment.class).value();
        }
    }

    private void setConstraint(FlowContext flowContext){
        Entity entity = (Entity) flowContext.checkData("entity");
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        Field field = (Field) flowContext.checkData("field");
        Property property = (Property) flowContext.checkData("property");

        Constraint constraint = field.getDeclaredAnnotation(Constraint.class);
        if (null != constraint) {
            property.notNull = constraint.notNull();
            if (null != property.check) {
                if (!property.check.isEmpty() && !property.check.contains("(")) {
                    property.check = "(" + property.check + ")";
                }
                property.check = property.check.replace("#{" + property.name + "}", property.column);
                //TODO 后续看看能不能去掉check属性
                property.escapeCheck = property.check.replace(property.column, quickDAOConfig.databaseContext.databaseProvider.escape(property.column));
            }
            property.defaultValue = constraint.defaultValue();
        }
        Id id = field.getDeclaredAnnotation(Id.class);
        if (null != id) {
            property.id = true;
            property.strategy = id.strategy();
        }
        TableField tableField = field.getDeclaredAnnotation(TableField.class);
        if (null != tableField) {
            if (!tableField.function().isEmpty()) {
                String databaseName = quickDAOConfig.databaseContext.databaseProvider.name();
                if("sqlite".equalsIgnoreCase(databaseName)
                        ||"h2".equalsIgnoreCase(databaseName)
                        ||"sqlserver".equalsIgnoreCase(databaseName)
                ){
                    flowContext.remark("sqlite,h2,sqlserver数据库不支持指定function属性!");
                }else{
                    property.function = tableField.function().replace("#{" + property.name + "}", "?");
                }
            }
            property.createdAt = tableField.createdAt();
            if(property.createdAt){
                property.comment = "创建时间";
            }
            property.updateAt = tableField.updatedAt();
            if(property.updateAt){
                property.comment = "更新时间";
            }
        }
        property.foreignKey = field.getDeclaredAnnotation(ForeignKey.class);
        if (property.id) {
            entity.id = property;
            property.notNull = true;
            property.comment = "自增id";
            //@Id注解生成策略为默认值又在全局指定里Id生成策略则使用全局策略
            if (property.strategy == IdStrategy.AutoIncrement && null != quickDAOConfig.entityOption.idStrategy) {
                property.strategy = quickDAOConfig.entityOption.idStrategy;
            }
        }
        if (null != property.foreignKey) {
            entity.foreignKeyProperties.add(property);
        }
    }

    private void setIndexField(FlowContext flowContext){
        Entity entity = (Entity) flowContext.checkData("entity");
        Field field = (Field) flowContext.checkData("field");
        Property property = (Property) flowContext.checkData("property");

        List<Index> indexList = new ArrayList<>();
        if (null != field.getDeclaredAnnotation(Index.class)) {
            indexList.add(field.getDeclaredAnnotation(Index.class));
        }
        Indexes indexes = field.getDeclaredAnnotation(Indexes.class);
        if (null != indexes && indexes.value().length > 0) {
            indexList.addAll(Arrays.asList(indexes.value()));
        }
        for (Index index : indexList) {
            IndexField indexField = new IndexField();
            indexField.tableName = entity.tableName;
            indexField.indexType = index.indexType();
            if (!index.indexName().isEmpty()) {
                indexField.indexName = index.indexName();
            } else {
                indexField.indexName = entity.tableName + "_" + indexField.indexType.name().toLowerCase() + "_" + property.column;
            }
            indexField.using = index.using();
            indexField.comment = index.comment();
            indexField.columns.add(property.column);
            entity.indexFieldList.add(indexField);
        }
    }
}